define(['jquery', 'pixi', 'rect', 'square', 'squareFactory'], function ($, PIXI, rect, square, squareFactory) {

    // 游戏地图(16x24)
    let gameData = [
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    ];

    // 界面中的各个区域
    let winRect = rect.WindowRect,
        workRect = new rect.AutoFitRect(winRect, {x: 1, y: 2}),
        gameRect = new rect.Rectangle(workRect, rect.Padding.all(10)),
        topRect = new rect.Rectangle({
            x: gameRect.x,
            y: gameRect.y,
            width: gameRect.width,
            height: Math.floor(gameRect.height / 4)
        }, rect.Padding.all(10));

    // 方块大小
    const cellSize = topRect.width / gameData[0].length;

    // 方块游戏区
    let mainRect = new rect.Rectangle({
        x: gameRect.x,
        y: gameRect.y + topRect.height + topRect.padding.top + topRect.padding.bottom,
        width: gameRect.width,
        height: cellSize * gameData.length
    }, new rect.Padding(10, 0, 10, 0));

    // P1游戏方块产生区
    let p1NextRect = new rect.Rectangle({
        x: topRect.x + cellSize * 2,
        y: topRect.y + topRect.height / 4,
        width: cellSize * 4,
        height: cellSize * 4
    });
    // P2游戏方块产生区
    let p2NextRect = new rect.Rectangle({
        x: topRect.x + topRect.width - cellSize * 6,
        y: p1NextRect.y,
        width: p1NextRect.width,
        height: p1NextRect.height
    });

    // 定义游戏配色(http://www.peise.net/)
    const Colors = {
        background: {line: 0x82A6F5, fill: 0x82A6F5},
        default: {line: 0x2E68AA, fill: 0xFFFFFF},
        P1: {
            active: {line: 0x77C34F, fill: 0x9FF048},
            inactive: {line: 0x2A5200, fill: 0x56A36C}
        },
        P2: {
            active: {line: 0xC1194E, fill: 0xFF5983},
            inactive: {line: 0x7A023C, fill: 0xC1194E}
        }
    };

    // 背景绘制
    let graphics = new PIXI.Graphics();
    // 方块产生区绘制
    let nextPainter = new PIXI.Graphics();
    // 方块游戏区域绘制
    let motionPainter = new PIXI.Graphics();

    // --------------- ------------------- --------------------

    let init = function (args) {
        let app = new PIXI.Application({
            width: winRect.width,   // default: 800
            height: winRect.height, // default: 600
            antialias: true,        // default: false
            transparent: false,     // default: false
            resolution: 1           // default: 1
        });
        $(document.body).attr("style", "margin:0px");
        $(document.body).append(app.view);
        app.stage.addChild(graphics);
        app.stage.addChild(nextPainter);
        app.stage.addChild(motionPainter);

        refreshBg();
        return app;
    };

    // --------------- ------------------- --------------------

    // down
    var down = function (player) {
        if (player.current.canMove(square.Direction.DOWN, isValid)) {
            setData(player, true);
            player.current.move(square.Direction.DOWN);
            setData(player);
            refreshGame();
            return true;
        } else {
            return false;
        }
    };
    // left
    var left = function (player) {
        if (player.current.canMove(square.Direction.LEFT, isValid)) {
            setData(player, true);
            player.current.move(square.Direction.LEFT);
            setData(player);
            refreshGame();
        }
    };
    // right
    var right = function (player) {
        if (player.current.canMove(square.Direction.RIGHT, isValid)) {
            setData(player, true);
            player.current.move(square.Direction.RIGHT);
            setData(player);
            refreshGame();
        }
    };
    // rotate
    var rotate = function (player) {
        if (player.current.canRotate(isValid)) {
            setData(player, true);
            player.current.rotate();
            setData(player);
            refreshGame();
        }
    };
    // fixed
    var fixed = function (player) {
        if (!player.current.canMove(square.Direction.DOWN, isValid)) {
            if (player.current.canFixed(fixedTest)) {
                let isRevert = player.current.isRevert,
                    value = (isRevert ? -1 : 1);
                for (var i = 0; i < player.current.data.length; i++) {
                    for (var j = 0; j < player.current.data[0].length; j++) {
                        if (check(player.current.origin, i, j, isRevert)) {
                            if (gameData[player.current.origin.x + i][player.current.origin.y + j] == (2 * value))
                                gameData[player.current.origin.x + i][player.current.origin.y + j] = value;
                        }
                    }
                }

                refreshGame();
                return true;
            }
        }

        return false;
    };

    // --------------- ------------------- --------------------

    // generateNext
    let generateNext = function (player, type, index) {
        player.current = player.next;
        setData(player);
        player.next = squareFactory.make(type, index);
        if (player.id != 1)
            player.next.revert();

        drawTetris(player.next.data, (player.id == 1 ? p1NextRect.pos : p2NextRect.pos));
        refreshGame();
    };

    // checkClear
    let checkClear = function () {
        var lines = 0;
        for (var i = gameData.length - 1; i >= 0; i--) {
            var clear = true;
            for (var j = 0; j < gameData[0].length; j++) {
                if (!(gameData[i][j] == 1 || gameData[i][j] == -1)) {
                    clear = false;
                    break;
                }
            }
            if (clear) {
                lines++;
                for (var m = i; m > 0; m--) {
                    for (var n = 0; n < gameData[0].length; n++) {
                        gameData[m][n] = gameData[m - 1][n];
                    }
                }
                for (var k = 0; k < gameData[0].length; k++) {
                    gameData[0][k] = 0;
                }
                i++;
            }
        }
        return lines;
    };

    // checkGameOver
    let checkGameOver = function () {
        var gameOver = false;
        for (var i = 0; i < gameData[0].length; i++) {
            if (gameData[0][i] == 1 || gameData[0][i] == -1) {
                gameOver = true;
                break;
            }
        }
        return gameOver;
    };

    // Check
    let check = function (pos, x, y, isRevert) {
        return !((pos.x + x < 0)
        || pos.x + x >= gameData.length
        || pos.y + y < 0 || pos.y + y >= gameData[0].length
        || gameData[pos.x + x][pos.y + y] == 1
        || gameData[pos.x + x][pos.y + y] == -1
        || gameData[pos.x + x][pos.y + y] == (isRevert ? 2 : -2));
    };

    // isValid
    let isValid = function (pos, data, isRevert) {
        for (var i = 0; i < data.length; i++) {
            for (var j = 0; j < data[0].length; j++) {
                if (data[i][j] != 0) {
                    if (!check(pos, i, j, isRevert)) {
                        return false;
                    }
                }
            }
        }
        return true;
    };

    // fixedTest
    let fixedTest = function (pos, data, isRevert) {
        for (var i = 0; i < data.length; i++) {
            for (var j = 0; j < data[0].length; j++) {
                if (data[i][j] != 0) {
                    if (((pos.x + i < 0)
                        || pos.x + i >= gameData.length
                        || pos.y + j < 0 || pos.y + j >= gameData[0].length
                        || gameData[pos.x + i][pos.y + j] == 1
                        || gameData[pos.x + i][pos.y + j] == -1)) {
                        return true;
                    }
                }
            }
        }
        return false;
    };

    // SetData
    let setData = function (player, reset) {
        let src = player.current;
        for (var i = 0; i < src.data.length; i++) {
            for (var j = 0; j < src.data[0].length; j++) {
                if (check(src.origin, i, j, src.isRevert)) {
                    gameData[src.origin.x + i][src.origin.y + j] = reset ? 0 : src.data[i][j];
                }
            }
        }
    };

    // --------------- ------------------- --------------------

    // gameOver
    let gameOver = function (win) {
        if (win) {
            console.log("你赢了!");
        } else {
            console.log("你输了!");
        }
    };

    // --------------- ------------------- --------------------

    // 刷新游戏区
    let refreshGame = function () {
        motionPainter.clear();
        drawTetris(motionPainter, gameData, mainRect.pos);
    };

    let refreshNext = function () {
        if (players.length > 0) {
            nextPainter.clear();
            players.forEach(p => {
                drawTetris(nextPainter, p.next.data, p.pos);
            });
        }
    };

    // 刷新游戏背景
    let refreshBg = function () {
        graphics.clear();
        // 窗口背景
        draw(graphics, winRect, Colors.background.fill, Colors.background.line);
        //draw(graphics, workRect, Colors.default.fill, Colors.default.line);
        draw(graphics, gameRect, Colors.default.fill, Colors.default.line);
        draw(graphics, topRect, Colors.default.fill, Colors.default.line);
        // 游戏区
        draw(graphics, mainRect, Colors.default.fill, Colors.default.line);
        // 方块产生区
        draw(graphics, p1NextRect, Colors.default.fill, Colors.default.line);
        draw(graphics, p2NextRect, Colors.default.fill, Colors.default.line);
    };

    // 绘制特定区域
    let draw = function (g, rect, fillColor, lineColor) {
        if (lineColor) {
            g.lineStyle(1, lineColor, 1);
        }
        g.beginFill(fillColor, 1);
        g.drawRect(rect.x, rect.y, rect.width, rect.height);
        g.endFill();
    };

    // 绘制方块
    let drawTetris = function (g, data, pos) {
        let x, y, v;
        let cell = cellSize - 2;
        for (let i = 0; i < data.length; i++) {
            y = cellSize * i + pos.y;
            for (let j = 0; j < data[0].length; j++) {
                x = cellSize * j + pos.x;
                v = data[i][j];
                switch (v) {
                    case 1:
                        draw(g, {
                            x: x + 1,
                            y: y + 1,
                            width: cell,
                            height: cell
                        }, Colors.P1.inactive.fill, Colors.P1.inactive.line);
                        break;
                    case -1:
                        draw(g, {
                            x: x + 1,
                            y: y + 1,
                            width: cell,
                            height: cell
                        }, Colors.P2.inactive.fill, Colors.P2.inactive.line);
                        break;
                    case 2:
                        draw(g, {
                            x: x + 1,
                            y: y + 1,
                            width: cell,
                            height: cell
                        }, Colors.P1.active.fill, Colors.P1.active.line);
                        break;
                    case -2:
                        draw(g, {
                            x: x + 1,
                            y: y + 1,
                            width: cell,
                            height: cell
                        }, Colors.P2.active.fill, Colors.P2.active.line);
                        break;
                    default:
                        draw(g, {
                            x: x + 1,
                            y: y + 1,
                            width: cell,
                            height: cell
                        }, Colors.default.fill, Colors.default.fill);
                        break;
                }
            }
        }
    };

    // --------------- ------------------- --------------------

    let Player = function () {

        this.id = undefined;
        this.current = undefined;
        this.next = undefined;
        this.pos = undefined;

        // generateNext
        this.generateNext = function (type, index) {
            this.current = this.next;
            setData(this);
            this.next = squareFactory.make(type, index);
            if (this.id == 2) {
                this.next.revert();
                this.next.origin.y = 10;
            }

            // 刷新游戏
            refreshNext();
            refreshGame();
        };

        this.down = function () {
            return down(this);
        };

        this.right = function () {
            right(this);
        };

        this.left = function () {
            left(this);
        };

        this.rotate = function () {
            rotate(this);
        };

        this.fall = function () {
            while (down(this));
        };

        this.fixed = function () {
            return fixed(this);
        };
    };

    let players = [];

    let createPlayer = function (type, index) {
        let player = null;
        let size = players.length;
        if (size < 2) {
            player = new Player();
            // 只允许两名玩家
            if (size == 0) {
                player.id = 1;
                player.pos = p1NextRect.pos;
                player.next = squareFactory.make(type, index);
                players.push(player);
            } else if (size == 1) {
                player.id = 2;
                player.pos = p2NextRect.pos;
                player.next = squareFactory.make(type, index);
                player.next.revert();
                player.next.origin.y = 10;
                players.push(player);
            }
        }

        return player;
    };

    // --------------- ------------------- --------------------

    return {
        init: init,
        checkClear: checkClear,
        checkGameOver: checkGameOver,
        gameOver: gameOver,
        createPlayer: createPlayer,
        Player: Player
    };
});